// UPnP .NET Framework Control Point Stack, Discovery Module
// Device Builder Build#1.0.4561.18413

using System;
using System.Net;
using OpenSource.UPnP;
using OpenSource.Utilities;
using MediaClient;
using System.Collections.Generic;
using MediaServer;
using System.Xml;
using MediaServer.Utilities;

namespace MediaClient
{
	/// <summary>
	/// Summary description for MediaServerDiscovery.
	/// </summary>
	class MediaControlPoint
    {
        private List<UPnPDevice> devices;
        private UPnPSmartControlPoint scp;
        private WeakEvent updateEvent = new WeakEvent();
        
        // TODO: content subscribe should be per device,
        // but for now global event will be enough.
        private WeakEvent contentEvent = new WeakEvent();

        public MediaControlPoint()
        {
        }

        public delegate void DevicesHandler();

        public event DevicesHandler OnUpdatedDeviceList
        {
            add
            {
                updateEvent.Register(value);
            }
            remove
            {
                updateEvent.UnRegister(value);
            }
        }

        public delegate void ContentHandler(MediaControlPoint Sender, IDIDLSerializable[] Results);

        public event ContentHandler OnUpdatedContentList
        {
            add
            {
                contentEvent.Register(value);
            }
            remove
            {
                contentEvent.UnRegister(value);
            }
        }
		
		public void Start()
		{
            devices = new List<UPnPDevice>();
            scp = new UPnPSmartControlPoint(new UPnPSmartControlPoint.DeviceHandler(OnAddDevice), null, "urn:schemas-upnp-org:device:MediaServer:1");
			scp.OnRemovedDevice += new UPnPSmartControlPoint.DeviceHandler(OnRemoveDevice);
		}

        public List<UPnPDevice> Devices()
        {
            return devices;
        }

		public void ReScan()
		{
			scp.Rescan();
		}
		
		public void UnicastSearch(IPAddress address)
		{
			scp.UnicastSearch(address);
		}
		
		public void ForceDisposeDevice(UPnPDevice dev)
		{
			if (dev.ParentDevice != null)
			{
				ForceDisposeDevice(dev.ParentDevice);
			}
			else
			{
				scp.ForceDisposeDevice(dev);
			}
		}

        public bool IsContentDirectory(UPnPDevice device)
        {
            return device.GetServices(ControlPointContentDirectory.SERVICE_NAME).Length > 0;
        }

        public void BrowseDevice(UPnPDevice Device, uint StartingIndex, uint RequestedCount)
        {
            BrowseDevice(Device, "0", StartingIndex, RequestedCount);
        }

        public void BrowseDevice(UPnPDevice Device, VirtualDirectory Container, uint StartingIndex, uint RequestedCount)
        {
            BrowseDevice(Device, Container.GetID(), StartingIndex, RequestedCount);
        }

        private void BrowseDevice(UPnPDevice Device, string Id, uint StartingIndex, uint RequestedCount)
        {
            UPnPService[] services = Device.GetServices(ControlPointContentDirectory.SERVICE_NAME);
            if (services.Length > 0)
            {
                ControlPointContentDirectory service = new ControlPointContentDirectory(services[0]);
                service.Browse(Id, ControlPointContentDirectory.Enum_A_ARG_TYPE_BrowseFlag.BROWSEDIRECTCHILDREN, "", StartingIndex, RequestedCount, "", null, OnBrowseResult);
            }
            else
            {
                throw new NotSupportedException("ContentDirectory service is not supported by this device.");
            }
        }

        private string GetAttr(XmlNode node, string key)
        {
            string value = "";
            foreach (XmlNode attr in node.ChildNodes)
            {
                if (attr.Name == key)
                {
                    value = attr.InnerText;
                }
            }
            return value;
        }

        private void OnBrowseResult(ControlPointContentDirectory sender, String ObjectID, ControlPointContentDirectory.Enum_A_ARG_TYPE_BrowseFlag BrowseFlag, String Filter, UInt32 StartingIndex, UInt32 RequestedCount, String SortCriteria, String Result, UInt32 NumberReturned, UInt32 TotalMatches, UInt32 UpdateID, UPnPInvokeException e, object _Tag)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(Result);
            XmlNode root = doc.ChildNodes[0];
            IDIDLSerializable[] Results = new IDIDLSerializable[root.ChildNodes.Count];

            int i = 0;
            foreach(XmlNode node in root.ChildNodes)
            {
                if (node.Name.Equals("container"))
                {
                    string title = GetAttr(node, "dc:title");
                    Results[i++] = new VirtualDirectory(title, node.Attributes["parentID"].Value, node.Attributes["id"].Value);
                }
                else if (node.Name.Equals("item"))
                {
                    string url = GetAttr(node, "res");
                    string title = GetAttr(node, "dc:title");
                    Results[i++] = new VirtualFile(title, node.Attributes["parentID"].Value, node.Attributes["id"].Value, url);
                }
            }

            contentEvent.Fire(this, Results);
        }

        private void OnAddDevice(UPnPSmartControlPoint sender, UPnPDevice device)
		{
            devices.Add(device);
            updateEvent.Fire();
		}
		
		private void OnRemoveDevice(UPnPSmartControlPoint sender, UPnPDevice device)
		{
            devices.Remove(device);
            updateEvent.Fire();
		}
	}
}

